package smallfrench.moteur.biblio;

import java.util.HashMap;

import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;

/**
 * Représente une classe avec un système d’héritage multiple.
 * 
 * @author antoine1023
 */
public class RClasse extends RObjet {

	/**
	 * La classe mère de tous les objets.
	 */
	public static final RClasse CLASSE = new RClasse();

	/**
	 * Les champs non statiques de la classe.
	 */
	protected final HashMap<String, RObjet> champs;

	/**
	 * Les champs statiques
	 */
	private final HashMap<String, RObjet> champsStatiques;

	/**
	 * <code>true</code> si la classe est abstraite et ne peut être instanciée.
	 */
	public final boolean estAbstraite;

	/**
	 * <code>true</code> si cette classe est native, c’est à dire écrite en
	 * Java.
	 */
	public final boolean estIntegree;

	/**
	 * Les classes parentes.
	 */
	protected final RClasse[] parentes;



	/**
	 * Ce constructeur privé ne doit être utilisé que pour créér la classe
	 * mère de tous les objets.
	 */
	private RClasse() {
		super(false);

		parentes = new RClasse[0];
		champs = new HashMap<String, RObjet>();
		champsStatiques = new HashMap<String, RObjet>();
		estAbstraite = true;
		estIntegree = true;
	}

	/**
	 * Créée une nouvelle classe intrinsèque non abstraite.
	 * @param parentes
	 */
	protected RClasse(RClasse[] parentes) {
		super(CLASSE);

		this.parentes = parentes;
		champs = new HashMap<String, RObjet>();
		champsStatiques = new HashMap<String, RObjet>();
		estAbstraite = false;
		estIntegree = true;
	}

	/**
	 * Créée une nouvelle classe
	 * @param parentes
	 * @param methodes
	 * @param champsStatiques
	 * @param estAbstraite
	 * @param estIntegree
	 */
	protected RClasse(
			RClasse[] parentes,
			HashMap<String, RObjet> methodes,
			HashMap<String, RObjet> champsStatiques,
			boolean estAbstraite,
			boolean estIntegree) {

		super(CLASSE);

		this.parentes = parentes;
		this.champs = methodes;
		this.champsStatiques = champsStatiques;
		this.estAbstraite = estAbstraite;
		this.estIntegree = estIntegree;
	}


	protected final void ajouterChampStatique(String nom, RObjet valeur) {
		champsStatiques.put(nom, valeur);
	}


	@Override
	public RChaine enChaine() throws RException {
		return new RChaine("Classe");
	}

	@Override
	public final boolean equals(Object objet) {
		return objet == this;
	}

	public final boolean estUne(RClasse c) {
		if (this == c) return true;
		for (RClasse i : parentes) {
			if (i.estUne(c)) return true;
		}
		return false;
	}

	@Override
	public final RObjet getMembre(String nom) {
		return champsStatiques.get(nom);
	}

	/**
	 * @param nom
	 * @return <code>null</code> si abstraite ou indéfinie.
	 */
	public final RObjet getMethode(String nom) {

		if (champs.containsKey(nom)) {
			return champs.get(nom);
		}

		for (RClasse rc : parentes) {
			if (rc instanceof AClasse) {
				AClasse ac = (AClasse) rc;
				RObjet f = ac.getMethode(nom);
				if (f != null) return f;
			}
		}

		return null;
	}

	/**
	 * @return Les superclasses, les classes parentes.
	 */
	public final RClasse[] getSuperClasses() {
		return parentes;
	}


	public boolean heriteDe(RClasse c) {
		for (RClasse i : getSuperClasses()) {
			if (i == c || i.heriteDe(c)) return true;
		}
		return false;
	}
}
